package com.wolfeyes.common.utils.framework.spring;

import java.lang.reflect.InvocationTargetException;

import com.wolfeyes.common.utils.character.StringUtils;
//import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.ApplicationContext;
import org.springframework.util.ReflectionUtils;

//import com.bootdo.common.config.common.ApplicationContextRegister;
import com.wolfeyes.common.utils.character.ParamUtils;
//import com.bootdo.utils.common.datetime.DateUtils;

/**
 * @title 参考常见问题FAQ文档：Spring bean 手工依赖注入
 * @core 核心级别  ❤❤❤❤❤
 * @description Spring框架下容器动态自动装配Bean的工具类
 * @projectName film
 * @className SpringDynamicAutowireBeanUtils.java
 * @author yanyljava
 * @email yanyljava@qq.com
 * @date 2019-01-19 11:09:58
 * @since 2019年01月19日下午5:37:39
 * @createTime 2019年01月19日 下午17:04:13
 * @version 1.0.0
 */
public class SpringDynamicAutowireBeanUtils {

	/**
	 * slf4j日志对象
	 */
	private static final Logger logger = LoggerFactory.getLogger(SpringDynamicAutowireBeanUtils.class);
	
	/**
	 * @title 采用依赖注入：Spring容器自动装配 
	 * @description Spring容器应用上下文环境 
	 */
	//@Autowired
	//@Qualifier("applicationContext")
	//private static ApplicationContext applicationContext; 
	
	/**
	 * @title 获取defaultListableBeanFactory 
	 * @description 获取默认的线性Bean工厂 - defaultListableBeanFactory 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return DefaultListableBeanFactory 默认的线性Bean工厂 
	 * @throws Exception 异常 
	 */
	public static DefaultListableBeanFactory getDefaultListableBeanFactory(ApplicationContext applicationContext) {
		
		// 创建defaultListableBeanFactory的对象引用
		DefaultListableBeanFactory defaultListableBeanFactory = null;
		
		// 判断非空
		//if(!ParamUtils.isNull(applicationContext)) {
			
			// 通过Spring容器应用上下文环境-ApplicationContext获得BeanFactory
			// 方法一：应用有些情况通过此方式拿到的beanFactory是null，特别是在应用第三方框架时，在dubbo中就遇到过 
			//BeanFactory beanFactory = applicationContext.getParentBeanFactory(); 
			//DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)beanFactory;
			
			// 方法二：主要用于存储Spring容器管理之外的Bean，Spring内部则很少使用 
			AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
			
			// 强制类型转换 
			defaultListableBeanFactory = (DefaultListableBeanFactory)autowireCapableBeanFactory;
			
		//} else {
			
			// 通过Spring容器应用上下文环境-ApplicationContext获得BeanFactory
			// 方法一：应用有些情况通过此方式拿到的beanFactory是null，特别是在应用第三方框架时，在dubbo中就遇到过 
			//BeanFactory beanFactory = applicationContext.getParentBeanFactory(); 
			//DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)beanFactory;
			
			// 方法二：主要用于存储Spring容器管理之外的Bean，Spring内部则很少使用 
			//AutowireCapableBeanFactory autowireCapableBeanFactory = SpringDynamicAutowireBeanUtils.applicationContext.getAutowireCapableBeanFactory();
			
			// 强制类型转换 
			//defaultListableBeanFactory = (DefaultListableBeanFactory)autowireCapableBeanFactory;
		//}
		
		return defaultListableBeanFactory;
	}
	
	/**
	 * @title 动态注册bean到Spring容器当中 
	 * @description 将bean以单例Singleton的形式注入到Spring容器当中 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @param beanClazz Bean类的字节码
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return String beanName 所要注入的bean名称 
	 * @throws Exception 异常 
	 */
	public static String registerBeanForSingleton(ApplicationContext applicationContext, 
		Class<? extends Object> beanClazz) { 
		
		// 创建对象的引用
		Object beanInstance = null;
		
		try {
			
			// 详参：QuartzJobFactory.createJobInstance方法的父类实现
			// 获取定时任务实例
			//beanInstance = ReflectionUtils.accessibleConstructor(beanClazz).newInstance();
			beanInstance = beanClazz.newInstance();
			
		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException 
				/*| InvocationTargetException | NoSuchMethodException*/ e) {
			// TODO 自动生成的 catch 块
			e.printStackTrace();
		}
		
		// 动态注册bean到Spring容器当中
		String beanName = SpringDynamicAutowireBeanUtils.registerBeanForSingleton(applicationContext, beanInstance);
		
		return beanName;
	}
	
	/**
	 * @title 动态注册bean到Spring容器当中 
	 * @description 将bean以单例Singleton的形式注入到Spring容器当中 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @param existingBean Spring容器当中已存在的bean实例对象beanInstance 
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return String beanName 所要注入的bean名称 
	 * @throws Exception 异常 
	 */
	public static String registerBeanForSingleton(ApplicationContext applicationContext, Object existingBean) {
		
		// 获取源代码中给出的底层类名的简称SimpleName
		String simpleName = existingBean.getClass().getSimpleName();
		
		// bean名称（首字母小写转换toLowerCase）
		String beanName = StringUtils.uncapitalize(simpleName);
		
		// 获取autowireCapableBeanFactory
		//AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
		
		// 获取defaultListableBeanFactory 
		DefaultListableBeanFactory defaultListableBeanFactory = SpringDynamicAutowireBeanUtils.getDefaultListableBeanFactory(applicationContext);
		
		//autowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(existingBean, beanName);
		defaultListableBeanFactory.applyBeanPostProcessorsAfterInitialization(existingBean, beanName);
		
		// 将bean以单例Singleton的形式注入到Spring容器当中 
		defaultListableBeanFactory.registerSingleton(beanName, existingBean);
		
		// 方法一：
		// 进行spring-bean依赖注入，spring容器会自动装配此Bean实例的相关属性 
		//autowireCapableBeanFactory.autowireBean(existingBean);
		//defaultListableBeanFactory.autowireBean(existingBean);
		
		// 方法二：
		// 调用spring上下文自动装配注入新创建对象existingBean的相关属性（根据属性名称进行注入）
		SpringDynamicAutowireBeanUtils.autowireBean(applicationContext, existingBean);
		
		// 开始做bean属性的实时测试：
		//applicationContext.getBean(existingBean.getClass());
		//applicationContext.getBean("commonJob");
		//autowireCapableBeanFactory.getBean("commonJob");
		//defaultListableBeanFactory.getBean("commonJob");
		
		return beanName;
	}
	
	/**
	 * @title 动态注册bean到Spring容器当中 
	 * @description 将bean以单例Singleton的形式注入到Spring容器当中 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @param existingBean Spring容器当中已存在的bean实例对象beanInstance 
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return String beanName 所要注入的bean名称 
	 * @throws Exception 异常 
	 */
	public static String registerBeanForSingleton(ApplicationContext applicationContext, Object existingBean, String beanName) {
		
		// 获取源代码中给出的底层类名的简称SimpleName
		//String simpleName = existingBean.getClass().getSimpleName();
		
		// bean名称（首字母小写转换toLowerCase）
		//String beanName = StringUtils.uncapitalize(simpleName);
		
		// 获取autowireCapableBeanFactory
		//AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
		
		// 获取defaultListableBeanFactory 
		DefaultListableBeanFactory defaultListableBeanFactory = SpringDynamicAutowireBeanUtils.getDefaultListableBeanFactory(applicationContext);
		
		//autowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(existingBean, beanName);
		defaultListableBeanFactory.applyBeanPostProcessorsAfterInitialization(existingBean, beanName);
		
		// 将bean以单例Singleton的形式注入到Spring容器当中 
		defaultListableBeanFactory.registerSingleton(beanName, existingBean);
		
		// 方法一：
		// 进行spring-bean依赖注入，spring容器会自动装配此Bean实例的相关属性 
		//autowireCapableBeanFactory.autowireBean(existingBean);
		//defaultListableBeanFactory.autowireBean(existingBean);
		
		// 方法二：
		// 调用spring上下文自动装配注入新创建对象existingBean的相关属性（根据属性名称进行注入）
		SpringDynamicAutowireBeanUtils.autowireBean(applicationContext, existingBean);
		
		// 开始做bean属性的实时测试：
		//applicationContext.getBean(existingBean.getClass());
		//applicationContext.getBean("commonJob");
		//autowireCapableBeanFactory.getBean("commonJob");
		//defaultListableBeanFactory.getBean("commonJob");
		
		return beanName;
	}
	
	/**
	 * @title 动态注册bean到Spring容器当中 
	 * @description 将bean以原型Prototype的形式注入到Spring容器当中， 
	 * 	一般重复注入一个新Bean的情况较少，多数情况都是将已有的Bean注入到Spring容器当中 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @param beanClazz Bean类的字节码 
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return String beanName 所要注入的bean名称 
	 * @throws Exception 异常 
	 */
	public static String registerBeanForPrototype(ApplicationContext applicationContext, 
		Class<? extends Object> beanClazz) { 
		
		// 获取defaultListableBeanFactory 
		DefaultListableBeanFactory defaultListableBeanFactory = SpringDynamicAutowireBeanUtils.getDefaultListableBeanFactory(applicationContext);
		
		// 方法一：
		// 根据待注入对象的类型、创建一个新的bean、添加到Spring容器中
		// 注意BeanDefinition有不同的实现类，注意不同实现类应用的场景
		BeanDefinition beanDefinition = new GenericBeanDefinition();
		
		// 获取源代码中给出的底层类名的全称FullName
		String beanClassName = beanClazz.getName();
		
		// 获取源代码中给出的底层类名的简称SimpleName
		String simpleName = beanClazz.getSimpleName();
		
		// bean名称（首字母小写转换toLowerCase）
		String beanName = StringUtils.uncapitalize(simpleName);
		
		// 设置属性beanClassName
		beanDefinition.setBeanClassName(beanClassName);
		
		// 将bean以原型Prototype的形式注入到Spring容器当中 
		//defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition);
		
		// 方法二：
		// 通过BeanDefinitionBuilder去构建一个BeanDefinition对象
		BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(beanClazz);
		
		// 在给定名称下添加提供的属性值，主要适用于简易型数据类型。
		//beanDefinitionBuilder.addPropertyValue(name, value);
		
		// 在指定的属性下添加对指定bean名称的引用。@Bean(name="xxxbeanName")
		//beanDefinitionBuilder.addPropertyReference(name, xxxbeanName);
		
		// Bean的作用域在Spring容器当中主要用到两个：单例Singleton(默认)和原型Prototype
		//String scope = BeanDefinition.SCOPE_PROTOTYPE;
		
		// 设置bean的作用域scope
		//beanDefinitionBuilder.setScope(scope);
		
		// 获取beanDefinition
		AbstractBeanDefinition beanDefinition2 = beanDefinitionBuilder.getBeanDefinition();
		
		// 动态注册bean
		// 将bean以原型Prototype的形式注入到Spring容器当中 
		defaultListableBeanFactory.registerBeanDefinition(beanName, beanDefinition2);
		
		return beanName;
	}
	
	/**
	 * @title 自动装配注入新创建对象existingBean的相关属性 
	 * @description 调用spring上下文自动装配注入新创建对象existingBean的相关属性（根据属性名称进行注入） 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @param existingBean Spring容器当中已存在的bean实例对象beanInstance
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return void
	 * @throws Exception 异常 
	 */
	public static void autowireBean(ApplicationContext applicationContext, Object existingBean) {
		
		// 调用spring上下文注入新创建对象的相关属性（根据属性名称进行注入）
		SpringDynamicAutowireBeanUtils.autowireBean(applicationContext, existingBean, AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
	}
	
	/**
	 * @lookup 详参：常见问题FAQ：Spring bean 手工依赖注入 15行、21行 、30行 
	 * @title 自动装配注入新创建对象existingBean的相关属性 
	 * @description 调用spring上下文自动装配注入新创建对象existingBean的相关属性 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @param existingBean Spring容器当中已存在的bean实例对象beanInstance 
	 * @param autowireMode 装配模式 
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return void
	 * @throws Exception 异常 
	 */
	public static void autowireBean(ApplicationContext applicationContext, 
		Object existingBean, int autowireMode) {
		
		// 判断非空
		if(ParamUtils.isNull(existingBean) || ParamUtils.isNull(applicationContext)) {
			
			return ;
		}
		
		// 获取autowireCapableBeanFactory
		AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
		
		// 调用spring上下文自动装配注入新创建对象existingBean的相关属性
		autowireCapableBeanFactory.autowireBeanProperties(existingBean, autowireMode, false);
		
		// 获取源代码中给出的底层类名的简称SimpleName
		String simpleName = existingBean.getClass().getSimpleName();
		
		// bean名称（首字母小写转换toLowerCase）
		String beanName = StringUtils.uncapitalize(simpleName);
		
		// 初始化existingBean，并设置beanName
		autowireCapableBeanFactory.initializeBean(existingBean, beanName);
	}
	
	/**
	 * @title 通过beanName去Spring容器当中 动态删除Bean 
	 * @description 调用spring上下文 动态删除Bean 
	 * @param beanName Spring容器当中已存在的bean实例对象beanInstance名称BeanName 
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return void 
	 * @throws Exception 异常 
	 */
	public static void removeBean(String beanName) {
		
		// 获取defaultListableBeanFactory
		DefaultListableBeanFactory defaultListableBeanFactory = SpringDynamicAutowireBeanUtils.getDefaultListableBeanFactory(null);
		
		// 相对于动态注入，动态删除就相对很简单
		// 动态删除bean
		defaultListableBeanFactory.removeBeanDefinition(beanName);
	}
	
	/**
	 * @title 从Spring容器当中，动态获取Bean 
	 * @description 将bean以单例Singleton的形式注入到Spring容器当中 
	 * @param applicationContext Spring容器应用上下文环境 
	 * @param beanName Spring容器当中已存在的bean实例对象beanInstance名称BeanName 
	 * @param beanClazz Bean类的字节码 
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @return Object beanInstance bean实例对象 
	 * @throws Exception 异常 
	 */
	public static Object getBeanForSecurity(ApplicationContext applicationContext, 
		String beanName, Class<? extends Object> beanClazz) {
		
		Object beanInstance = null;
		
		try {
			
			// 若Spring容器当中bean实例对象不存在，则会抛出异常BeansException
			// 通过beanName，从SpringBean容器上下文中获取bean实例对象beanInstance 
			beanInstance = applicationContext.getBean(beanName);
			
		} catch (BeansException e) {
			
			// TODO: handle exception
			// 在Spring容器当中新建一个Bean实例对象： 
			// 将bean以单例Singleton的形式注入到Spring容器当中 
			SpringDynamicAutowireBeanUtils.registerBeanForSingleton(applicationContext, beanClazz); 
			
			// 开始做bean属性的实时测试：
			//this.applicationContext.getBean("commonJob");
			//this.applicationContext.getBean(CommonJob.class);
			
			// 通过beanName，从SpringBean容器上下文中获取bean实例对象beanInstance 
			beanInstance = applicationContext.getBean(beanName);
		}
		
		// 返回所获取的bean实例对象
		return beanInstance;
	}
	
	/**
	 * @title 详参：CommonJobBean.executeInternal方法的父类实现
	 * @author yanyljava 
	 * @since 2019年01月21日下午16:44:39 
	 * @see #getBeanForSecurity 
	 * @test 测试方法main()
	 */
    public void main() {
        
    	// 快速获取长格式的系统当前时间字符串；
    	//String currentTime = DateUtils.getTimeNow();
    	
        //System.out.println("SpringDynamicAutowireBeanUtils.main Hello World!!! -- " + currentTime); 
        
    }
    
}


